<template>
  <div class="file-upload-component">
    <el-upload
      ref="uploadRef"
      v-model:file-list="fileList"
      class="upload-area"
      :drag="drag"
      :multiple="multiple"
      :action="uploadUrl"
      :headers="uploadHeaders"
      :data="uploadData"
      :accept="accept"
      :auto-upload="autoUpload"
      :list-type="listType"
      :show-file-list="showFileList"
      :on-preview="handlePreview"
      :on-success="handleSuccess"
      :on-error="handleError"
      :on-remove="handleRemove"
      :on-exceed="handleExceed"
      :before-upload="beforeUpload"
      :limit="limit"
    >
      <div v-if="drag" class="upload-dragger-content">
        <el-icon class="el-icon--upload"><UploadFilled /></el-icon>
        <div class="el-upload__text">
          将文件拖到此处，或<em>点击上传</em>
        </div>
      </div>
      
      <el-button v-if="!drag" type="primary" :icon="Upload">
        {{ buttonText }}
      </el-button>
      
      <template #tip>
        <div v-if="tipText" class="el-upload__tip">
          {{ tipText }}
        </div>
      </template>
    </el-upload>
    
    <!-- 文件预览对话框 -->
    <el-dialog
      v-model="previewVisible"
      :title="previewFile?.name"
      width="80%"
      append-to-body
    >
      <div class="preview-content">
        <!-- 图片预览 -->
        <el-image
          v-if="previewFile && isImage(previewFile.url)"
          :src="previewFile.url"
          fit="contain"
          style="width: 100%; max-height: 500px"
        />
        
        <!-- PDF预览 -->
        <iframe
          v-else-if="previewFile && isPDF(previewFile.url)"
          :src="previewFile.url"
          style="width: 100%; height: 500px; border: none"
        />
        
        <!-- 其他文件类型 -->
        <div v-else-if="previewFile" class="file-preview">
          <el-icon class="file-icon"><Document /></el-icon>
          <p class="file-name">{{ previewFile.name }}</p>
          <p class="file-info">此文件类型暂不支持预览</p>
          <el-button type="primary" @click="downloadFile">
            <el-icon><Download /></el-icon>
            下载文件
          </el-button>
        </div>
      </div>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { ElMessage, ElMessageBox, type UploadInstance, type UploadFile, type UploadFiles } from 'element-plus'
import { UploadFilled, Upload, Document, Download } from '@element-plus/icons-vue'
import { useAuthStore } from '@/stores/auth'

interface Props {
  // 文件类型配置
  fileType?: 'image' | 'document' | 'all'
  // 上传配置
  multiple?: boolean
  drag?: boolean
  autoUpload?: boolean
  limit?: number
  maxSize?: number // MB
  // 显示配置
  listType?: 'text' | 'picture' | 'picture-card'
  showFileList?: boolean
  buttonText?: string
  tipText?: string
  // 初始文件列表
  initialFiles?: UploadFile[]
}

interface Emits {
  (e: 'success', file: UploadFile, response: any): void
  (e: 'error', error: Error, file: UploadFile): void
  (e: 'remove', file: UploadFile): void
  (e: 'change', fileList: UploadFiles): void
  (e: 'exceed', files: File[], fileList: UploadFiles): void
}

const props = withDefaults(defineProps<Props>(), {
  fileType: 'all',
  multiple: true,
  drag: true,
  autoUpload: true,
  limit: 10,
  maxSize: 10,
  listType: 'text',
  showFileList: true,
  buttonText: '选择文件',
  tipText: ''
})

const emit = defineEmits<Emits>()
const authStore = useAuthStore()

// 上传实例引用
const uploadRef = ref<UploadInstance>()

// 文件列表
const fileList = ref<UploadFiles>([])

// 预览相关
const previewVisible = ref(false)
const previewFile = ref<UploadFile | null>(null)

// 文件类型配置
type FileTypeKey = 'image' | 'document' | 'all'

const fileTypeConfig: Record<FileTypeKey, {
  accept: string
  mimeTypes: string[]
  description: string
}> = {
  image: {
    accept: '.jpg,.jpeg,.png,.gif,.bmp,.webp',
    mimeTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/webp'],
    description: '图片'
  },
  document: {
    accept: '.doc,.docx,.pdf,.ppt,.pptx,.xls,.xlsx,.txt,.zip,.rar',
    mimeTypes: [
      'application/pdf',
      'application/msword',
      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
      'application/vnd.ms-powerpoint',
      'application/vnd.openxmlformats-officedocument.presentationml.presentation',
      'application/vnd.ms-excel',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      'text/plain',
      'application/zip',
      'application/x-rar-compressed'
    ],
    description: '文档'
  },
  all: {
    accept: '.jpg,.jpeg,.png,.gif,.bmp,.webp,.doc,.docx,.pdf,.ppt,.pptx,.xls,.xlsx,.txt,.zip,.rar',
    mimeTypes: [],
    description: '文件'
  }
}

// 计算属性
const accept = computed(() => fileTypeConfig[props.fileType].accept)

const uploadUrl = computed(() => {
  // 开发环境下使用 Vite 代理，直接使用 /api 路径
  return '/api/files/upload'
})

const uploadHeaders = computed(() => ({
  Authorization: `Bearer ${authStore.token}`
}))

const uploadData = computed(() => ({
  type: props.fileType
}))

const tipText = computed(() => {
  if (props.tipText) return props.tipText
  
  const config = fileTypeConfig[props.fileType]
  const sizeText = `单个文件不超过 ${props.maxSize}MB`
  const limitText = props.limit > 1 ? `，最多上传 ${props.limit} 个文件` : ''
  
  return `支持上传 ${config.description} 格式文件，${sizeText}${limitText}`
})

// 监听初始文件列表
watch(() => props.initialFiles, (newFiles) => {
  if (newFiles && newFiles.length > 0) {
    fileList.value = [...newFiles]
  }
}, { immediate: true })

// 监听文件列表变化
watch(fileList, (newFileList) => {
  emit('change', newFileList)
}, { deep: true })

// 文件类型判断
const isImage = (url?: string) => {
  if (!url) return false
  return /\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(url)
}

const isPDF = (url?: string) => {
  if (!url) return false
  return /\.pdf$/i.test(url)
}

// 上传前校验
const beforeUpload = (file: File) => {
  // 文件类型校验
  const config = fileTypeConfig[props.fileType]
  if (config.mimeTypes.length > 0 && !config.mimeTypes.includes(file.type)) {
    ElMessage.error(`只能上传 ${config.description} 格式文件！`)
    return false
  }
  
  // 文件大小校验
  const isLtMaxSize = file.size / 1024 / 1024 < props.maxSize
  if (!isLtMaxSize) {
    ElMessage.error(`上传文件大小不能超过 ${props.maxSize}MB！`)
    return false
  }
  
  return true
}

// 上传成功
const handleSuccess = (response: any, file: UploadFile, fileList: UploadFiles) => {
  if (response.success && response.data) {
    file.url = response.data.url
    file.response = response
    ElMessage.success('文件上传成功')
    emit('success', file, response)
  } else {
    handleError(new Error(response.message || '上传失败'), file, fileList)
  }
}

// 上传失败
const handleError = (error: Error, file: UploadFile, fileList: UploadFiles) => {
  ElMessage.error(error.message || '文件上传失败')
  emit('error', error, file)
}

// 移除文件
const handleRemove = (file: UploadFile, fileList: UploadFiles) => {
  return ElMessageBox.confirm(
    '确定要删除该文件吗？',
    '删除确认',
    {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    }
  ).then(() => {
    emit('remove', file)
    return true
  }).catch(() => {
    return false
  })
}

// 超出文件数量限制
const handleExceed = (files: File[], fileList: UploadFiles) => {
  ElMessage.warning(`最多只能上传 ${props.limit} 个文件，当前选择了 ${files.length} 个文件，已上传 ${fileList.length} 个文件`)
  emit('exceed', files, fileList)
}

// 预览文件
const handlePreview = (file: UploadFile) => {
  previewFile.value = file
  previewVisible.value = true
}

// 下载文件
const downloadFile = () => {
  if (previewFile.value?.url) {
    const link = document.createElement('a')
    link.href = previewFile.value.url
    link.download = previewFile.value.name || ''
    link.target = '_blank'
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
  }
}

// 清空文件列表
const clearFiles = () => {
  uploadRef.value?.clearFiles()
}

// 手动上传
const submit = () => {
  uploadRef.value?.submit()
}

// 获取文件列表
const getFileList = () => {
  return fileList.value
}

// 获取成功上传的文件URL列表
const getUrls = () => {
  return fileList.value
    .filter(file => file.status === 'success' && file.url)
    .map(file => file.url!)
}

// 暴露方法
defineExpose({
  clearFiles,
  submit,
  getFileList,
  getUrls
})
</script>

<style scoped>
.file-upload-component {
  width: 100%;
}

.upload-area {
  width: 100%;
}

.upload-dragger-content {
  text-align: center;
  padding: 40px 20px;
}

:deep(.el-upload-dragger) {
  padding: 40px 20px;
  background-color: #fafbfc;
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: all 0.3s;
}

:deep(.el-upload-dragger:hover) {
  border-color: #409eff;
  background-color: rgba(64, 158, 255, 0.06);
}

:deep(.el-icon--upload) {
  font-size: 67px;
  color: #c0c4cc;
  margin-bottom: 16px;
  line-height: 50px;
}

:deep(.el-upload__text) {
  color: #606266;
  font-size: 14px;
  text-align: center;
}

:deep(.el-upload__text em) {
  color: #409eff;
  font-style: normal;
}

:deep(.el-upload__tip) {
  font-size: 12px;
  color: #606266;
  margin-top: 7px;
  text-align: center;
}

.preview-content {
  text-align: center;
}

.file-preview {
  padding: 40px;
  text-align: center;
}

.file-icon {
  font-size: 67px;
  color: #c0c4cc;
  margin-bottom: 16px;
}

.file-name {
  font-size: 16px;
  font-weight: 500;
  color: #303133;
  margin-bottom: 8px;
}

.file-info {
  color: #909399;
  font-size: 14px;
  margin-bottom: 20px;
}

:deep(.el-dialog__body) {
  padding: 20px;
}
</style>